﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Threading;
using BrainTechLLC.ImageOperators;
using System.Diagnostics;
using System.Drawing;
using BrainTechLLC;
using BrainTechLLC.WPF;
using BrainTechLLC.ThreadSafeObjects;
using System.Windows;
using BrainTechLLC.ImageOperatorsNonAForgeDependent;
using System.IO;
using System.Drawing.Imaging;

namespace ImageAppDataModels
{
    public partial class ThumbnailProvider : BaseNotifyPropertyChangedUI
    {
        public static ThreadSafeLookup<string, SingleThumbnail> ThumbnailLookup = new ThreadSafeLookup<string, SingleThumbnail>();
        public static ThreadSafeCounter ThumbsInMemory = new ThreadSafeCounter();
        public static ThreadSafeCounter ThumbsReadFromDisk = new ThreadSafeCounter();
        public static ThreadSafeCounter ThumbsReadFromImageBytes = new ThreadSafeCounter();

        public static ICoreDataProvider DefaultDataProvider;
        public ICoreDataProvider _DataProvider;
        public event EventHandler<ObjectEventArgs<IHasBitmap, SingleThumbnail>> SingleThumbnailLoaded;

        public ICoreDataProvider DataProvider
        {
            get
            {
                if (_DataProvider == null)
                {
                    _DataProvider = DefaultDataProvider;
                }
                return _DataProvider;
            }
            set
            {
                if (_DataProvider != value)
                {
                    _DataProvider = value;
                }
            }
        }

        public int GetThumbsInMemory()
        {
            return ThumbsInMemory.Count;
        }

        public SingleThumbnail PreloadThumbnail(string fileName, FileImage fileImage)
        {
            if (!fileImage.Deleted)
            {
                var obj = fileImage.ThumbnailObject;
                if (obj == null)
                {
                    Debug.WriteLine("Preloading thumb for " + fileName);
                    return GetThumbnail(fileName, LoadOptions.LoadSync, fileImage, true, (fi, thumb) =>
                    {
                        ThumbsReadFromImageBytes.Increment();
                        string fn = fileName.GetThumbFileName();

                        if (!fileImage.DataProvider.Exists(fn))
                        {
                            try
                            {
                                var tn = thumb.ThumbnailCached;

                                if (tn != null)
                                {
                                    tn.Save(fn);
                                }
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.ToString());
                            }
                        }
                    });
                }

                return obj;
            }
            else
            {
                return null; // TODO
            }
        }

        public SingleThumbnail GetThumbnail(string fileName, LoadOptions loadOptions, IHasBitmap hasBitmap, bool needThumbBytes, Action<IHasBitmap, SingleThumbnail> callback)
        {
            SingleThumbnail thumb = ThumbnailLookup[fileName];

            if (thumb != null && (!needThumbBytes || thumb.ThumbnailCached != null))
            {
                return thumb;
            }

            if (thumb == null)
            {
                thumb = new SingleThumbnail() { FileName = fileName, HasBitmap = hasBitmap };
            }

            Bitmap bmThumb = null;

            if (loadOptions != LoadOptions.LoadAsync)
            {
                string fn = fileName.GetThumbFileName();
                bool create = true;

                try
                {
                    byte[] bytes = DataProvider.ReadAllThumbnailBytes(fn);
                    if (bytes != null)
                    {
                        ThumbnailInfo ti = bytes.DeserializeFromBinary(false) as ThumbnailInfo;
                        if (ti != null)
                        {
                            bmThumb = Image.FromStream(new MemoryStream(ti.ThumbBytes)) as Bitmap;
                            hasBitmap.OriginalBitmapHeight = ti.OriginalBitmapHeight;
                            hasBitmap.OriginalBitmapWidth = ti.OriginalBitmapWidth;
                            hasBitmap.FileSize = ti.OriginalFileSize;
                            hasBitmap.CreationTime = ti.OriginalFileDate;
                            ThumbsReadFromDisk.Increment();
                            ti.ThumbBytes = null;
                            create = false;
                            thumb.Thumbnail = bmThumb;
                        }
                    }
                }
                catch
                {
                }

                if (create)
                {
                    try
                    {
                        bmThumb = CreateAndSetThumbnail(hasBitmap, fileName);

                        if (bmThumb != null)
                        {
                            ThumbsReadFromImageBytes.Increment();
                            _dispatcher.BeginInvoke(new VoidDelegate(() =>
                            {
                                Bitmap bm = thumb.ThumbnailCached;

                                if (bm != null)
                                {
                                    try
                                    {
                                        if (hasBitmap.CreationTimeNoLoad == DateTime.MinValue)
                                        {
                                            hasBitmap.CreationTimeNoLoad = hasBitmap.GetFileSizeAndCreationTime();
                                        }
                                        ThumbnailInfo ti = new ThumbnailInfo()
                                        {
                                            OriginalBitmapWidth = hasBitmap.OriginalBitmapWidth,
                                            OriginalBitmapHeight = hasBitmap.OriginalBitmapHeight,
                                            OriginalFileDate = hasBitmap.CreationTime,
                                            OriginalFileSize = hasBitmap.FileSize
                                        };
                                        System.Drawing.Imaging.ImageCodecInfo codec;
                                        System.Drawing.Imaging.EncoderParameters param;
                                        JpegEncoding.GetJPEGParamsForConversionQuality(90, out codec, out param);

                                        using (MemoryStream ms = new MemoryStream())
                                        {
                                            bm.Save(ms, codec, param);
                                            ti.ThumbBytes = ms.ToArray();
                                        }
                                        byte[] allbytes = ti.SerializeToBinary();
                                        DataProvider.WriteAllBytes(fn, allbytes);
                                        DataProvider.SetAttributes(fn, FileAttributes.Hidden | FileAttributes.NotContentIndexed);
                                        ti.ThumbBytes = null;
                                    }
                                    catch (Exception ex)
                                    {
                                        Console.WriteLine(ex.ToString());
                                    }
                                }
                            }), System.Windows.Threading.DispatcherPriority.Background);
                        }
                        thumb.Thumbnail = bmThumb;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                    }
                }
            }
            else
            {
                ThumbnailLazyLoader._lazyLoaderThumbnail.LazyLoad(hasBitmap as FileImage, (fi) => callback(fi, thumb));
            }

            SingleThumbnailLoaded.RaiseEvent(this, hasBitmap, thumb);
            ThumbnailLookup.Add(fileName, thumb);
            return thumb;
        }

        public Bitmap CreateAndSetThumbnail(IHasBitmap fileImage, string fileName)
        {
            // may need to raise events after the fact                
            Bitmap bm = fileImage.EnsureBitmapLoaded();
            Bitmap bmThumb = HandleLoadThumbnail(bm, fileName, fileImage);

            return bmThumb;
        }

        public int TargetThumbs()
        {
            int target = Settings.Values.MaxThumbsInMemory - (Settings.Values.MaxThumbsInMemory / 6);

            if (target < 4)
                target = 4;

            return target;
        }

        protected Bitmap HandleLoadThumbnail(Bitmap bm, string fileName, IHasBitmap fileImage)
        {
            if (bm == null)
                return null;

            Debug.WriteLine("Rendered thumb for " + fileName);
            Bitmap bmOut;
            RenderSettings renderSettings;

            if (Settings.Values.HighQualityThumbs)
            {
                renderSettings = new RenderSettings()
                {
                    PixelFormat = Settings.Values.StandardColorPixelFormat,
                    Interpolation = Settings.Values.InterpolationMode,
                    Smoothing = Settings.Values.SmoothingMode,
                    RenderingMethod = Settings.Values.InternalRendering,
                    ForceHighQuality = Settings.Values.HighQualityThumbs
                };
            }
            else
            {
                renderSettings = Settings.Values.StandardRenderSettings;
            }

            bmOut = bm.ResizeBitmap(fileImage.OriginalBitmapWidth, fileImage.OriginalBitmapHeight, (int)Settings.Values.ThumbWidth, (int)Settings.Values.ThumbHeight, renderSettings);
            return bmOut;
        }

        //ThreadSafeQueue<ThreadSafeLookupOnDemandWithDelegate<string, FileImage>> ChachedImages = new NewsStyleUriParser();
    }
}
